
import json, logging, re, argparse, sqlite3, json, xml.etree.ElementTree as ET, sys
from pathlib import Path
from typing import Any
from jsmin import jsmin
from jinja2 import Template


#### =======================================================================================================
####     Open function
#### =======================================================================================================
class XPath:

    def __init__(self, path):
        """
        * path {str} URI路径字符串
        """

        assert path!="", "Parameter 'path' cannot be empty"
        self.fullpath = path

        self.paths = []
        self.protocol = "file"
        self.fields = []
        self.parameters = []
        self.name = ""
        self.ext = ""

        self.patt0 = r'(?:(?P<protocol>[\w]+):)?(?P<path>[\w./]+)(?P<field>[#][^?]+)?(?P<parameter>[?](.*))?'

        self.parse0()
        self.parse1()

    def parse0(self):
        """第一层解析。解析协议, 域，参数和路径"""

        m = re.match(self.patt0, self.fullpath)
        assert m, f'Cannot match Path: {self.fullpath}'

        path = m.group('path')
        if path: self.paths = [p.strip() for p in re.split(r'[\/\\]', path)]

        if m.group('protocol'): self.protocol = m.group('protocol')

        field = m.group('field')
        if field: self.fields = field[1:].split('/')      # 去除'#'后按'/'拆分

        parameter = m.group('parameter')
        if parameter: self.parameters = [cond.split('=') for cond in parameter[1:].split('&')]     # 去除'?'后按'&'拆分

    def parse1(self):
        path = self.paths[-1]
        p = path.rfind(".")
        if p:
            self.name = path[:p]
            self.ext = path[p+1:]
        else:
            self.name = path


def xread( path:str, log:logging.Logger = None ) -> str :

    #### 匹配路径
    patt = r'(?:(?P<protocol>[\w]+):)?(?P<path>[\w./]+)(?P<field>[#][^?]+)?(?P<parameter>[?](.*))?'
    m = re.match(patt, path)
    assert m, f'Cannot match Path: {str}'

    #### 处理匹配结果
    path = m.group('path')
    assert Path(path).exists(), f'Cannot open file: {path}'

    protocol = m.group('protocol')
    if not protocol: protocol = 'file'

    field = m.group('field')
    if field: field = field[1:].split('/')      # 去除'#'后按'/'拆分

    parameter = m.group('parameter')
    if parameter: parameter = [cond.split('=') for cond in parameter[1:].split('&')]     # 去除'?'后按'&'拆分

    if log: log.info(f'xread: protocol={protocol}; path={path}; field={field}; parameter={parameter}')

    rst = ""
    #### 分条件处理
    if protocol == "sqlite":
        assert field and parameter and len(field)==2 and len(parameter)==1, f'Cannot support <NULL> field and parameter for sqlite read'
        try:
            conn = sqlite3.connect(path)
            cur = conn.cursor()
            sql = f"""SELECT "{field[1]}" FROM "{field[0]}" WHERE "{parameter[0][0]}"='{parameter[0][1]}' """
            cur.execute(sql)
            r = cur.fetchone()
            assert r, f'Cannot find field for SQL: {sql}'
            rst = r[0]
            conn.close()
        except Exception:
            if log: log.fatal(f'Sqlite error for: {sql}')

    elif protocol == "file":
        with open(path, "r", encoding="utf-8") as f:
            rst = f.read()

    else:
        if log: log.fatal(f"Cannot support this protocol: {protocol}")

    return rst


def render_snippet(
        snippet:str, 
        params:dict, 
        tab:int,
        lang:str = ""
        ) -> str:
    if snippet:
        template = Template(snippet)
        snippet = template.render(**params)

    if snippet and tab > 0:
        repl = ''.join([" "]*tab)
        snippet = repl + re.sub( r'\n', f'\n{repl}', snippet )

    return snippet

def parse_command_string(cmd:str) -> dict:
    params = re.split(r'[   ]+', cmd.strip())
    patt = r"""[\b]*(?P<key>[^=\b]*)[\b]*=[\b]*(?P<quote>["'])(?P<value>.*)(?P=quote)[\b]*"""

    def split_key_val(param:str):
        m = re.match(patt, param)
        if m: return (m.group("key"), m.group("value").strip())
        return ()

    return dict([split_key_val(p) for p in params if len(p)>0])
